package com.trace.demo.aspect;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.trace.demo.annotation.OperationLog;
import com.trace.demo.repository.OperationLogRepository;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 操作日志记录切面
 */
@Aspect
@Component
public class OperationLogAspect {

    @Autowired
    private OperationLogRepository operationLogRepository;
    
    @Autowired
    private ObjectMapper objectMapper;
    
    // 定义响应结果最大长度，超过此长度将被截断
    private static final int MAX_RESPONSE_LENGTH = 65000; // 接近MySQL TEXT类型的最大长度

    /**
     * 定义切入点，拦截标记了@OperationLog注解的方法
     */
    @Pointcut("@annotation(com.trace.demo.annotation.OperationLog)")
    public void operationLogPointcut() {
        // 切入点定义
    }

    /**
     * 在方法正常返回后记录操作日志
     */
    @AfterReturning(pointcut = "operationLogPointcut()", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) {
        try {
            // 获取request对象
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                return;
            }
            HttpServletRequest request = attributes.getRequest();

            // 获取当前用户信息（这里简化处理，实际应从安全上下文或会话中获取）
            Long userId = null;
            String userName = "unknown";
            
            // 从session获取用户信息
            Object userObj = request.getSession().getAttribute("user");
            if (userObj != null) {
                // 根据实际用户对象类型获取用户ID和用户名
                // 这里假设是Map类型
                try {
                    if (userObj instanceof Map) {
                        Map<String, Object> userMap = (Map<String, Object>) userObj;
                        userId = Long.valueOf(userMap.get("id").toString());
                        userName = (String) userMap.get("username");
                    }
                } catch (Exception e) {
                    // 解析用户信息异常，使用默认值
                }
            }

            // 获取方法上的注解信息
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            OperationLog opLog = method.getAnnotation(OperationLog.class);

            // 将请求参数和响应结果转为JSON字符串，并确保不超出数据库字段长度限制
            String requestParams = limitLength(objectMapper.writeValueAsString(getRequestParams(joinPoint)));
            String responseResult = limitLength(objectMapper.writeValueAsString(result));

            // 创建操作日志对象
            com.trace.demo.entity.OperationLog log = com.trace.demo.entity.OperationLog.builder()
                    .userId(userId)
                    .userName(userName)
                    .ipAddress(getIpAddress(request))
                    .module(opLog.module())
                    .action(opLog.action())
                    .description(opLog.description())
                    .requestParams(requestParams)
                    .responseResult(responseResult)
                    .result(true)
                    .operationTime(LocalDateTime.now())
                    .createdAt(LocalDateTime.now())
                    .build();

            // 保存操作日志
            operationLogRepository.save(log);
        } catch (Exception e) {
            // 记录日志异常，不影响业务
            e.printStackTrace();
        }
    }

    /**
     * 在方法抛出异常后记录操作日志
     */
    @AfterThrowing(pointcut = "operationLogPointcut()", throwing = "ex")
    public void doAfterThrowing(JoinPoint joinPoint, Exception ex) {
        try {
            // 获取request对象
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                return;
            }
            HttpServletRequest request = attributes.getRequest();

            // 获取当前用户信息（这里简化处理，实际应从安全上下文或会话中获取）
            Long userId = null;
            String userName = "unknown";
            
            // 从session获取用户信息
            Object userObj = request.getSession().getAttribute("user");
            if (userObj != null) {
                // 根据实际用户对象类型获取用户ID和用户名
                try {
                    if (userObj instanceof Map) {
                        Map<String, Object> userMap = (Map<String, Object>) userObj;
                        userId = Long.valueOf(userMap.get("id").toString());
                        userName = (String) userMap.get("username");
                    }
                } catch (Exception e) {
                    // 解析用户信息异常，使用默认值
                }
            }

            // 获取方法上的注解信息
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            OperationLog opLog = method.getAnnotation(OperationLog.class);

            // 将请求参数和异常信息转为JSON字符串，并确保不超出数据库字段长度限制
            String requestParams = limitLength(objectMapper.writeValueAsString(getRequestParams(joinPoint)));
            String responseResult = limitLength(objectMapper.writeValueAsString(ex.getMessage()));

            // 创建操作日志对象
            com.trace.demo.entity.OperationLog log = com.trace.demo.entity.OperationLog.builder()
                    .userId(userId)
                    .userName(userName)
                    .ipAddress(getIpAddress(request))
                    .module(opLog.module())
                    .action(opLog.action())
                    .description(opLog.description())
                    .requestParams(requestParams)
                    .responseResult(responseResult)
                    .result(false)
                    .operationTime(LocalDateTime.now())
                    .createdAt(LocalDateTime.now())
                    .build();

            // 保存操作日志
            operationLogRepository.save(log);
        } catch (Exception e) {
            // 记录日志异常，不影响业务
            e.printStackTrace();
        }
    }

    /**
     * 获取请求参数
     */
    private Object getRequestParams(JoinPoint joinPoint) {
        // 获取方法参数值
        Object[] args = joinPoint.getArgs();
        // 获取方法参数名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String[] parameterNames = signature.getParameterNames();
        
        // 构建参数键值对
        Map<String, Object> params = new LinkedHashMap<>();
        for (int i = 0; i < parameterNames.length; i++) {
            params.put(parameterNames[i], args[i]);
        }
        return params;
    }

    /**
     * 获取客户端IP地址
     */
    private String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
    
    /**
     * 限制字符串长度，防止数据库字段溢出
     * 
     * @param str 需要限制长度的字符串
     * @return 限制长度后的字符串
     */
    private String limitLength(String str) {
        if (str != null && str.length() > MAX_RESPONSE_LENGTH) {
            // 截断字符串并添加省略号
            return str.substring(0, MAX_RESPONSE_LENGTH - 3) + "...";
        }
        return str;
    }
} 